
#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"

PUBLIC int kernel_main() {
    disp_str("-----\"kernel_main\" begins-----\n");

	struct task* p_task;
	struct proc* p_proc= proc_table;
    char*    p_task_stack = task_stack + STACK_SIZE_TOTAL;
    u16      selector_ldt = SELECTOR_LDT_FIRST;
    int i;

    u8 privilege;
    u8 rpl;
    int eflags;
    int prio;


    for(i = 0; i < NR_TASKS + NR_PROCS; i++) {

        if(i < NR_TASKS) { // 区分了 TASK 和 PROC，即区分了任务和用户进程
            p_task = task_table + i;
            privilege = PRIVILEGE_TASK;
            rpl = RPL_TASK;
            eflags = 0x1202; // IF=1, IOPL=1, bit 2 is always 1
            prio = 15;
        } else {
            p_task = user_proc_table + (i - NR_TASKS);
            privilege = PRIVILEGE_USER;
            rpl = RPL_USER;
            eflags = 0x202; // IF=1, bit 2 is always 1
            prio = 5;
        }

        strcpy(p_proc->name, p_task -> name);
        p_proc -> pid = i;

        p_proc -> ldt_sel = selector_ldt; // 设置 LDT Selector

        // -------------------------------------------------------
        // 主要变化：修改用户进程特权级、剥夺用户进程所有 I/O 权限

        // 将 SELECTOR_KERNEL_CS 所指向的描述符拷贝到进程 PCB 的 LDTS[0] 处
        memcpy(&p_proc -> ldts[0], &gdt[SELECTOR_KERNEL_CS >> 3], sizeof(struct descriptor));
        p_proc -> ldts[0].attr1 = DA_C | privilege << 5; // 设置属性，更改 DPL
        // 将 SELECTOR_KERNEL_DS 所指向的描述符拷贝到进程 PCB 的 LDTS[1] 处
        memcpy(&p_proc -> ldts[1], &gdt[SELECTOR_KERNEL_DS >> 3], sizeof(struct descriptor));
        p_proc -> ldts[1].attr1 = DA_DRW | privilege << 5; // 设置属性，更改 DPL
        // Tips：右移 3 位表示去掉选择子后面的 TI 和 RPL，留下描述符索引

        // 构建选择子，选择子结构：描述符索引(15~3) TI(2) RPL(1~0)
        // LDT 共有两个描述符,分别被初始化成内核代码段和内核数据段，只是改变了一下 DPL 使其运行在低特权级下

        // CS 指向第一个描述符
        p_proc -> regs.cs = ((8 * 0) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
        // 其它的指向第二个描述符
        p_proc -> regs.ds = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
        p_proc -> regs.es = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
        p_proc -> regs.fs = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
        p_proc -> regs.ss = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
        // gs 仍然指向显存，只是改变了 RPL
        p_proc -> regs.gs = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | rpl;
        // TestA 这是最先开始运行的
        p_proc -> regs.eip = (u32) p_task -> initial_eip;
        // esp 表示 TestA 这个程序运行所需的栈
        p_proc -> regs.esp = (u32) p_task_stack;
        // 设置标志位，IF=1, IOPL=1, 第二位总是为 1
        // 设置 IOPL 后进程就可以使用 I/O 指令了
        // 并且中断会在 iretd 执行时被打开（之前在 kernel.asm 中 sti 被注释掉了，这里会自动打开）
        p_proc -> regs.eflags = eflags;

        p_proc -> nr_tty = 0; // 默认所有用户进程都绑定 0 号控制台

		p_proc->p_flags = 0;
		p_proc->p_msg = 0;
		p_proc->p_recvfrom = NO_TASK;
		p_proc->p_sendto = NO_TASK;
		p_proc->has_int_msg = 0;
		p_proc->q_sending = 0;
		p_proc->next_sending = 0;

		p_proc->ticks = p_proc->priority = prio; // 初始化 ticks and priority

        p_task_stack -= p_task -> stacksize;
        p_proc++;
        p_task++;
        selector_ldt += 1 << 3;
    }

    proc_table[NR_TASKS + 0].nr_tty = 0;
    proc_table[NR_TASKS + 1].nr_tty = 1;
    proc_table[NR_TASKS + 2].nr_tty = 1;    k_reenter = 0; // 判断是否中断嵌套的全局变量

    ticks = 0;

    p_proc_ready = proc_table;
    
    // 初始化各种中断
    init_clock(); // 初始化 8253 PIT
    init_keyboard(); // 初始化键盘中断

    /*disp_pos = 0;
    for(i = 0; i < 80 * 25; i++)
        disp_str(" ");
    disp_pos = 0;*/

    restart();

    while(1);
}

/*****************************************************************************
 *                                panic
 *****************************************************************************/
PUBLIC void panic(const char *fmt, ...) {
	int i;
	char buf[256];

	/* 4 是 fmt 在堆栈中的大小 */
	va_list arg = (va_list)((char*)&fmt + 4);

	i = vsprintf(buf, fmt, arg);

	printl("%c !!panic!! %s", MAG_CH_PANIC, buf);

	/* 永远不应该到达这里 */
	__asm__ __volatile__("ud2");
}

PUBLIC int get_ticks() {
    MESSAGE msg;
    reset_msg(&msg);
    msg.type = GET_TICKS;
    send_recv(BOTH, TASK_SYS, &msg);
    return msg.RETVAL;
}

void TestA() {
    for(;;);
    while(1) {
        printf("<Ticks:%x>", get_ticks());
        milli_delay(200);
    }
}

void TestB() {
    while(1) {
        printf("B");
        milli_delay(200);
    }
}

void TestC() {
    // assert(0);
    while(1) {
        printf("C");
        milli_delay(200);
    }
}

